﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Objects.DataClasses;
using System.Data.Objects;

namespace EntityFrameworkContrib.PostSharp4EF
{
    /// <summary>
    /// implementation of the interface IRelationshipLoader
    /// Makes it possible to get to EDM type relationships
    /// </summary>
    [Serializable]
	public class RelationshipLoaderImplementation : IRelationshipLoader
	{
        private readonly object instance;

        /// <summary>
        /// Initializes a new instance of the <see cref="RelationshipLoaderImplementation"/> class.
        /// </summary>
        /// <param name="instance">The instance.</param>
        public RelationshipLoaderImplementation(object instance)
        {
            this.instance = instance;
        }


        /// <summary>
        /// Loads a relationship
        /// </summary>
        /// <param name="PropertyName">Name of the property pointing to your relation.</param>
        /// <remarks>will use a mergeoption of 'preserveChanges'</remarks>
        public void Load(string PropertyName)
        {
            Load(PropertyName, MergeOption.PreserveChanges);
        }

        /// <summary>
        /// Loads the specified relation name.
        /// </summary>
        /// <param name="PropertyName">Name of the property pointing to your relation.</param>
        /// <param name="mergeOption">The merge option.</param>
        public void Load(string PropertyName, MergeOption mergeOption)
        {
            IRelatedEnd relatedEnd = GetRelatedEnd(PropertyName);

            relatedEnd.Load(mergeOption);
        }

        /// <summary>
        /// Gets the related end.
        /// </summary>
        /// <param name="PropertyName">Name of the property.</param>
        /// <returns>IRelatedEnd</returns>
        /// <remarks>by using this method, your code will rely on EF specific implementations.
        /// Try to use more generic methods, like Load and IsLoaded, which are quite generic</remarks>
        public IRelatedEnd GetRelatedEnd(string PropertyName)
        {
            IEntityWithRelationships relationEntity = (IEntityWithRelationships)instance;

            // need the relatedend and targetrolename, and will get it from the attribute above the property
            EdmRelationshipNavigationPropertyAttribute nav = instance.GetType().GetProperty(PropertyName).GetCustomAttributes(typeof(EdmRelationshipNavigationPropertyAttribute), true).FirstOrDefault() as EdmRelationshipNavigationPropertyAttribute;

            if (nav == null)
                throw new ArgumentOutOfRangeException("Property is not a relation navigation");

            IRelatedEnd relatedEnd = relationEntity.RelationshipManager.GetRelatedEnd(nav.RelationshipName, nav.TargetRoleName);
            return relatedEnd;
        }

        /// <summary>
        /// Determines whether the specified relationship is loaded.
        /// </summary>
        /// <param name="PropertyName">Name of the property pointing to your relation.</param>
        /// <returns>
        /// 	<c>true</c> if the specified property name is loaded; otherwise, <c>false</c>.
        /// </returns>
        public bool IsLoaded(string PropertyName)
        {
            IRelatedEnd relatedEnd = GetRelatedEnd(PropertyName);
            return relatedEnd.IsLoaded;
        }

        /// <summary>
        /// Gets the related collection.
        /// </summary>
        /// <typeparam name="T">the type of the instances in the collection</typeparam>
        /// <param name="PropertyName">Name of the property.</param>
        /// <returns></returns>
        public EntityCollection<T> GetRelatedCollection<T>(string PropertyName)
            where T : class, IEntityWithRelationships
        {
            IRelatedEnd relatedEnd = GetRelatedEnd(PropertyName);
            return relatedEnd as EntityCollection<T>;
        }

        /// <summary>
        /// Gets the related reference.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="PropertyName">Name of the property.</param>
        /// <returns></returns>
        public EntityReference<T> GetRelatedReference<T>(string PropertyName)
            where T : class, IEntityWithRelationships
        {
            IRelatedEnd relatedEnd = GetRelatedEnd(PropertyName);
            return relatedEnd as EntityReference<T>;
        }
    }
}
